"""
插件钩子系统

实现插件的钩子机制，允许插件在特定时机执行代码。
"""

import asyncio
from typing import Any, Dict, List, Optional, Callable, Union
from abc import ABC, abstractmethod
from enum import Enum
import inspect

from .exceptions import PluginHookError


class HookType(Enum):
    """钩子类型枚举"""
    
    BEFORE = "before"    # 前置钩子
    AFTER = "after"      # 后置钩子
    AROUND = "around"    # 环绕钩子
    ERROR = "error"      # 错误钩子
    FILTER = "filter"    # 过滤钩子


class HookPriority(Enum):
    """钩子优先级枚举"""
    
    HIGHEST = 1
    HIGH = 25
    NORMAL = 50
    LOW = 75
    LOWEST = 100


class PluginHook:
    """
    插件钩子
    
    定义插件钩子的基本结构。
    """
    
    def __init__(self, 
                 name: str,
                 hook_type: HookType,
                 handler: Callable,
                 priority: int = HookPriority.NORMAL.value,
                 plugin_name: Optional[str] = None,
                 conditions: Optional[Dict[str, Any]] = None):
        self.name = name
        self.hook_type = hook_type
        self.handler = handler
        self.priority = priority
        self.plugin_name = plugin_name
        self.conditions = conditions or {}
        self.enabled = True
        
        # 分析处理器签名
        self.is_async = asyncio.iscoroutinefunction(handler)
        self.signature = inspect.signature(handler)
    
    def can_execute(self, context: Dict[str, Any]) -> bool:
        """检查是否可以执行钩子"""
        if not self.enabled:
            return False
        
        # 检查条件
        for key, expected_value in self.conditions.items():
            if key not in context or context[key] != expected_value:
                return False
        
        return True
    
    async def execute(self, *args, **kwargs) -> Any:
        """执行钩子"""
        try:
            if self.is_async:
                return await self.handler(*args, **kwargs)
            else:
                return self.handler(*args, **kwargs)
        except Exception as e:
            raise PluginHookError(
                f"钩子执行失败: {e}",
                plugin_name=self.plugin_name,
                hook_name=self.name
            )
    
    def __str__(self) -> str:
        return f"Hook({self.name}, {self.hook_type.value}, priority={self.priority})"
    
    def __repr__(self) -> str:
        return self.__str__()


class HookManager:
    """
    钩子管理器
    
    管理所有插件钩子的注册、执行和生命周期。
    """
    
    def __init__(self):
        self._hooks: Dict[str, List[PluginHook]] = {}
        self._hook_points: Dict[str, Dict[str, Any]] = {}
        self._execution_stats: Dict[str, Dict[str, Any]] = {}
    
    def register_hook_point(self, 
                           name: str, 
                           description: str = "",
                           parameters: Optional[Dict[str, type]] = None,
                           return_type: Optional[type] = None) -> None:
        """注册钩子点"""
        self._hook_points[name] = {
            "description": description,
            "parameters": parameters or {},
            "return_type": return_type,
            "registered_at": asyncio.get_event_loop().time()
        }
    
    def register_hook(self, hook_point: str, hook: PluginHook) -> None:
        """注册钩子"""
        if hook_point not in self._hooks:
            self._hooks[hook_point] = []
        
        # 按优先级插入
        inserted = False
        for i, existing_hook in enumerate(self._hooks[hook_point]):
            if hook.priority < existing_hook.priority:
                self._hooks[hook_point].insert(i, hook)
                inserted = True
                break
        
        if not inserted:
            self._hooks[hook_point].append(hook)
    
    def unregister_hook(self, hook_point: str, hook_name: str, plugin_name: Optional[str] = None) -> bool:
        """注销钩子"""
        if hook_point not in self._hooks:
            return False
        
        for i, hook in enumerate(self._hooks[hook_point]):
            if (hook.name == hook_name and 
                (plugin_name is None or hook.plugin_name == plugin_name)):
                del self._hooks[hook_point][i]
                return True
        
        return False
    
    def unregister_plugin_hooks(self, plugin_name: str) -> int:
        """注销插件的所有钩子"""
        removed_count = 0
        
        for hook_point in self._hooks:
            hooks_to_remove = []
            for i, hook in enumerate(self._hooks[hook_point]):
                if hook.plugin_name == plugin_name:
                    hooks_to_remove.append(i)
            
            # 从后往前删除，避免索引问题
            for i in reversed(hooks_to_remove):
                del self._hooks[hook_point][i]
                removed_count += 1
        
        return removed_count
    
    def get_hooks(self, hook_point: str, hook_type: Optional[HookType] = None) -> List[PluginHook]:
        """获取钩子列表"""
        if hook_point not in self._hooks:
            return []
        
        hooks = self._hooks[hook_point]
        
        if hook_type:
            hooks = [hook for hook in hooks if hook.hook_type == hook_type]
        
        return hooks
    
    def has_hooks(self, hook_point: str, hook_type: Optional[HookType] = None) -> bool:
        """检查是否有钩子"""
        return len(self.get_hooks(hook_point, hook_type)) > 0
    
    async def execute_before_hooks(self, hook_point: str, context: Dict[str, Any], *args, **kwargs) -> Dict[str, Any]:
        """执行前置钩子"""
        hooks = self.get_hooks(hook_point, HookType.BEFORE)
        
        for hook in hooks:
            if hook.can_execute(context):
                try:
                    result = await hook.execute(context, *args, **kwargs)
                    if result is not None:
                        context.update(result if isinstance(result, dict) else {"result": result})
                except Exception as e:
                    self._record_hook_error(hook_point, hook.name, e)
                    raise
        
        return context
    
    async def execute_after_hooks(self, hook_point: str, context: Dict[str, Any], result: Any, *args, **kwargs) -> Any:
        """执行后置钩子"""
        hooks = self.get_hooks(hook_point, HookType.AFTER)
        
        for hook in hooks:
            if hook.can_execute(context):
                try:
                    hook_result = await hook.execute(context, result, *args, **kwargs)
                    if hook_result is not None:
                        result = hook_result
                except Exception as e:
                    self._record_hook_error(hook_point, hook.name, e)
                    raise
        
        return result
    
    async def execute_filter_hooks(self, hook_point: str, context: Dict[str, Any], data: Any, *args, **kwargs) -> Any:
        """执行过滤钩子"""
        hooks = self.get_hooks(hook_point, HookType.FILTER)
        
        for hook in hooks:
            if hook.can_execute(context):
                try:
                    filtered_data = await hook.execute(context, data, *args, **kwargs)
                    if filtered_data is not None:
                        data = filtered_data
                except Exception as e:
                    self._record_hook_error(hook_point, hook.name, e)
                    raise
        
        return data
    
    async def execute_error_hooks(self, hook_point: str, context: Dict[str, Any], error: Exception, *args, **kwargs) -> Optional[Any]:
        """执行错误钩子"""
        hooks = self.get_hooks(hook_point, HookType.ERROR)
        
        for hook in hooks:
            if hook.can_execute(context):
                try:
                    result = await hook.execute(context, error, *args, **kwargs)
                    if result is not None:
                        return result  # 错误被处理
                except Exception as e:
                    self._record_hook_error(hook_point, hook.name, e)
                    # 继续执行其他错误钩子
        
        return None
    
    async def execute_around_hooks(self, hook_point: str, context: Dict[str, Any], 
                                  original_func: Callable, *args, **kwargs) -> Any:
        """执行环绕钩子"""
        hooks = self.get_hooks(hook_point, HookType.AROUND)
        
        if not hooks:
            # 没有环绕钩子，直接执行原函数
            if asyncio.iscoroutinefunction(original_func):
                return await original_func(*args, **kwargs)
            else:
                return original_func(*args, **kwargs)
        
        # 构建钩子链
        def build_chain(hook_index: int):
            if hook_index >= len(hooks):
                # 链的末端，执行原函数
                async def final_func(*args, **kwargs):
                    if asyncio.iscoroutinefunction(original_func):
                        return await original_func(*args, **kwargs)
                    else:
                        return original_func(*args, **kwargs)
                return final_func
            
            hook = hooks[hook_index]
            next_func = build_chain(hook_index + 1)
            
            async def wrapper(*args, **kwargs):
                if hook.can_execute(context):
                    return await hook.execute(context, next_func, *args, **kwargs)
                else:
                    return await next_func(*args, **kwargs)
            
            return wrapper
        
        chain = build_chain(0)
        return await chain(*args, **kwargs)
    
    def _record_hook_error(self, hook_point: str, hook_name: str, error: Exception) -> None:
        """记录钩子错误"""
        if hook_point not in self._execution_stats:
            self._execution_stats[hook_point] = {}
        
        if hook_name not in self._execution_stats[hook_point]:
            self._execution_stats[hook_point][hook_name] = {
                "executions": 0,
                "errors": 0,
                "last_error": None
            }
        
        stats = self._execution_stats[hook_point][hook_name]
        stats["errors"] += 1
        stats["last_error"] = str(error)
    
    def get_hook_stats(self, hook_point: Optional[str] = None) -> Dict[str, Any]:
        """获取钩子统计信息"""
        if hook_point:
            return self._execution_stats.get(hook_point, {})
        return self._execution_stats
    
    def list_hook_points(self) -> List[str]:
        """列出所有钩子点"""
        return list(self._hook_points.keys())
    
    def list_hooks(self, hook_point: Optional[str] = None) -> Dict[str, List[str]]:
        """列出钩子"""
        if hook_point:
            if hook_point in self._hooks:
                return {hook_point: [hook.name for hook in self._hooks[hook_point]]}
            return {hook_point: []}
        
        result = {}
        for point, hooks in self._hooks.items():
            result[point] = [hook.name for hook in hooks]
        return result
    
    def enable_hook(self, hook_point: str, hook_name: str, plugin_name: Optional[str] = None) -> bool:
        """启用钩子"""
        return self._set_hook_enabled(hook_point, hook_name, True, plugin_name)
    
    def disable_hook(self, hook_point: str, hook_name: str, plugin_name: Optional[str] = None) -> bool:
        """禁用钩子"""
        return self._set_hook_enabled(hook_point, hook_name, False, plugin_name)
    
    def _set_hook_enabled(self, hook_point: str, hook_name: str, enabled: bool, plugin_name: Optional[str] = None) -> bool:
        """设置钩子启用状态"""
        if hook_point not in self._hooks:
            return False
        
        for hook in self._hooks[hook_point]:
            if (hook.name == hook_name and 
                (plugin_name is None or hook.plugin_name == plugin_name)):
                hook.enabled = enabled
                return True
        
        return False
    
    def clear_hooks(self, hook_point: Optional[str] = None) -> None:
        """清空钩子"""
        if hook_point:
            if hook_point in self._hooks:
                self._hooks[hook_point].clear()
        else:
            self._hooks.clear()
    
    def clear_stats(self) -> None:
        """清空统计信息"""
        self._execution_stats.clear()


# 全局钩子管理器
hook_manager = HookManager()


# 装饰器函数
def hook(hook_point: str, 
         hook_type: HookType = HookType.BEFORE,
         priority: int = HookPriority.NORMAL.value,
         conditions: Optional[Dict[str, Any]] = None):
    """钩子装饰器"""
    def decorator(func):
        hook_obj = PluginHook(
            name=func.__name__,
            hook_type=hook_type,
            handler=func,
            priority=priority,
            conditions=conditions
        )
        hook_manager.register_hook(hook_point, hook_obj)
        return func
    return decorator


def before_hook(hook_point: str, priority: int = HookPriority.NORMAL.value):
    """前置钩子装饰器"""
    return hook(hook_point, HookType.BEFORE, priority)


def after_hook(hook_point: str, priority: int = HookPriority.NORMAL.value):
    """后置钩子装饰器"""
    return hook(hook_point, HookType.AFTER, priority)


def filter_hook(hook_point: str, priority: int = HookPriority.NORMAL.value):
    """过滤钩子装饰器"""
    return hook(hook_point, HookType.FILTER, priority)


def error_hook(hook_point: str, priority: int = HookPriority.NORMAL.value):
    """错误钩子装饰器"""
    return hook(hook_point, HookType.ERROR, priority)
